package com.example.opengltest1;

import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class GLProgram {

    private final String tag = "GLES_program";

    private boolean isProgramBuild;

    private int _program = 0;

    public boolean isProgramBuild() {
        return isProgramBuild;
    }

    /********************
     * 准备赋值对象
     * ******************/
    int _positionHandle;
    int _coordHandle;
    int _matrixHandle;
    int _yhandle;
    int _uhandle;
    int _vhandle;
    public void buildProgram() {
        createBuffers(squareVertices, coordVertices);
        if (_program <= 0) {
            _program = createProgram(vertexShaderCode, fragmentShaderCode);
        }

        // 获取着色器程序内attribute、uniform变量操作句柄，便于后面进行赋值
        _positionHandle = GLES20.glGetAttribLocation(_program, "vPosition");    // 顶点位置参数
        _coordHandle = GLES20.glGetAttribLocation(_program, "a_texCoord");      // 纹理位置参数
        _matrixHandle = GLES20.glGetUniformLocation(_program, "uMVPMatrix");    // 旋转矩形参数
        _yhandle = GLES20.glGetUniformLocation(_program, "tex_y");  // y纹理参数
        _uhandle = GLES20.glGetUniformLocation(_program, "tex_u");  // u纹理参数
        _vhandle = GLES20.glGetUniformLocation(_program, "tex_v");  // v纹理参数

        isProgramBuild = true;
    }

    /***************
     * 准备赋值参数
     * ******************/
    int _image_width = 0;
    int _image_height = 0;
    int _ytid = 0;
    int _utid = 0;
    int _vtid = 0;
    float[] mvpMatrix = new float[16];
    public void buildTextures(Buffer y, Buffer u, Buffer v, int width, int height) {
        boolean imageSizeChanged = (width != _image_width || height != _image_height);
        if (imageSizeChanged) {
            _image_width = width;
            _image_height = height;
        }

        // 设置旋转矩阵
        float angle = 90;
        Matrix.setRotateM(mvpMatrix, 0, angle, 0, 0, -1);

        // building texture for Y data
        if (_ytid < 0 || imageSizeChanged) {
            if (_ytid >= 0) {
                GLES20.glDeleteTextures(1, new int[] {_ytid}, 0);
            }
            int[] textures = new int[1];
            // 生成纹理id，
            GLES20.glGenTextures(1, textures, 0);
            _ytid = textures[0];
        }
        // 操作纹理，添加纹理数据
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, _ytid);
        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, _image_width, _image_height, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, y);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);

        // building texture for U data
        if (_utid < 0 || imageSizeChanged) {
            if (_utid >= 0) {
                GLES20.glDeleteTextures(1, new int[] {_utid}, 0);
            }
            int[] textures = new int[1];
            GLES20.glGenTextures(1, textures, 0);
            _utid = textures[0];
        }
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, _utid);
        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, _image_width/2, _image_height/2, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, u);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);

        // building texture for V data
        if (_vtid < 0 || imageSizeChanged) {
            if (_vtid >= 0) {
                GLES20.glDeleteTextures(1, new int[] {_vtid}, 0);
            }
            int[] textures = new int[1];
            GLES20.glGenTextures(1, textures, 0);
            _vtid = textures[0];
        }
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, _vtid);
        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, _image_width/2, _image_height/2, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, v);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
    }

    /*********************
     * 赋值
     * ********************/
    public void drawFrame() {
        GLES20.glUseProgram(_program);
        // 传入顶点坐标
        GLES20.glVertexAttribPointer(_positionHandle, 2, GLES20.GL_FLOAT, false, 8, _vertice_buffer);
        GLES20.glEnableVertexAttribArray(_positionHandle);

        // 传入纹理坐标
        GLES20.glVertexAttribPointer(_coordHandle, 2, GLES20.GL_FLOAT, false, 8, _coord_buffer);
        GLES20.glEnableVertexAttribArray(_coordHandle);

        // 激活纹理单元0
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        // 绑定纹理id
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, _ytid);
        // 设置着色器中的纹理采样器
        GLES20.glUniform1i(_yhandle, 0);

        GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, _utid);
        GLES20.glUniform1i(_uhandle, 1);
//
        GLES20.glActiveTexture(GLES20.GL_TEXTURE2);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, _vtid);
        GLES20.glUniform1i(_vhandle, 2);

        GLES20.glUniformMatrix4fv(_matrixHandle, 1, false, mvpMatrix, 0);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0,4);
        GLES20.glFinish();

        GLES20.glDisableVertexAttribArray(_positionHandle);
        GLES20.glDisableVertexAttribArray(_coordHandle);
    }

    /*********************
     * 创建着色器程序
     * ******************/
    public int createProgram(String vertexSource, String fragmentSource) {
        // 编译顶点着色器程序
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
        // 编译片段着色器程序
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);

        int program = GLES20.glCreateProgram();
        if (program != 0) {
            GLES20.glAttachShader(program, vertexShader);   // 附加顶点着色器
            GLES20.glAttachShader(program, fragmentShader); // 附加片段着色器
            GLES20.glLinkProgram(program);                  // 链接程序
//            int[] linkStatus = new int[0];
//            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
//            if (linkStatus[0] != 1) {
//                Log.d(tag, "get program fail");
//                GLES20.glDeleteProgram(program);
//                program = 0;
//            }
        } else {
            Log.d(tag, "create program fail");
        }
        return program;
    }

    private int loadShader(int shaderType, String shaderCode) {
        int shader = GLES20.glCreateShader(shaderType);
        if (shader != 0) {
            GLES20.glShaderSource(shader, shaderCode);
            GLES20.glCompileShader(shader);
            int[] compiled = new int[1];
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
            if (compiled[0] == 0) {
                Log.d(tag, "compile shader fail");
                GLES20.glDeleteShader(shader);
                shader = 0;
            }
        } else {
            Log.d(tag, "create shader fail");
        }
        return shader;
    }

    /*********************
     * 为顶点坐标和纹理坐标分配缓冲区
     * ***********************/
    private ByteBuffer _vertice_buffer;
    private ByteBuffer _coord_buffer;
    private void createBuffers(float[] vert, float[] coord) {
        _vertice_buffer = ByteBuffer.allocateDirect(vert.length * 4);
        _vertice_buffer.order(ByteOrder.nativeOrder());
        _vertice_buffer.asFloatBuffer().put(vert);
        _vertice_buffer.position(0);

        if (_coord_buffer == null) {
            _coord_buffer = ByteBuffer.allocateDirect(coord.length * 4);
            _coord_buffer.order(ByteOrder.nativeOrder());
            _coord_buffer.asFloatBuffer().put(coord);
            _coord_buffer.position(0);
        }
    }

    /*
    * 需要将纹理图贴到顶点图上，所有就存在纹理映射，需要将纹理坐标映射到顶点坐标上
    * */

    // 顶点坐标
    private static float[] squareVertices = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f};
    // 纹理坐标
    private static float[] coordVertices = {0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f};

    private static final String vertexShaderCode =
            "attribute vec4 vPosition;" +
            "attribute vec2 a_texCoord;" +
            "uniform mat4 uMVPMatrix;" +
            "varying vec2 tc;" +            // 纹理坐标
            "void main() {" +
            "   gl_Position = uMVPMatrix * vPosition;" +
            "   tc = a_texCoord;" +
            "}";
    private static final String fragmentShaderCode =
            "precision mediump float;" +    // 精度
            "uniform sampler2D tex_y;" +    // y纹理采样器
            "uniform sampler2D tex_u;" +    // u纹理采样器
            "uniform sampler2D tex_v;" +    // v纹理采样器
            "varying vec2 tc;" +
            "void main() {" +
            "   mediump vec3 yuv;" +
            "   lowp vec3 rgb;" +
            "   yuv.x = texture2D(tex_y, tc).r;" +
            "   yuv.y = texture2D(tex_v, tc).r - 0.5;" +
            "   yuv.z = texture2D(tex_u, tc).r - 0.5;" +
            "   rgb = mat3(1, 1, 1, 0, -0.39465, 2.03211, 1.13983, -0.58060, 0) * yuv;" +
            "   gl_FragColor = vec4(rgb, 1);" +
            "}";

}
